首页 > 试题广场 >

执行以下程序,输出结果为()

[单选题]
执行以下程序,输出结果为()
function Foo() {
    var i = 0;
    return function() {
        console.log(i++);
    }
}
var f1 = Foo(),
var f2 = Foo();
f1();
f1();
f2();



  • 0 1 0
  • 0 1 2
  • 0 0 0
  • 0 0 2
查了红宝石,要点如下:
(1)Function是引用类型:保存在堆中,变量f1,f2是保存在栈中;
(2)闭包:一个函数(产生新的作用域)定义的局部变量、子函数的作用域在函数内,
         但是一旦离开了这个函数,局部变量就无法访问,所有通过返回子函数到一个变量f1的方法,让
         f1指向堆中的函数作用域,这样可以使用局部变量i.
(3)   过程:
   第一次f1()  :f1=Foo()中,先执行Foo(): i = 0,return值返回给f1
 (f1指向子函数   f1()=function(){.....},因为子函数没有 定义i,所以向上找到父函数定义的 i )并执行子函数 输出i=0,再自加 i =1(覆盖了父函数Foo 的 i值);
  第二次f1() : 执行的是子函数 Function(){  ..},输出的是父函数 的 i=1,再自加 i =2;
  第一次f2():同第一次f1(),不同的是 f2指向堆中一个新的对象 function(){ ...},所有此i非彼i,输出i=0;如果
               如果再次f2(),那么和第二次f1(),一样输出i=1; 
     
发表于 2016-04-03 16:08:50 回复(31)

这道题考察闭包引用类型对象的知识点:
1.一般来说函数执行完后它的局部变量就会随着函数调用结束被销毁,但是此题foo函数返回了一个匿名函数的引用(即一个闭包),它可以访问到foo()被调用产生的环境,而局部变量i一直处在这个环境中,只要一个环境有可能被访问到,它就不会被销毁,所以说闭包有延续变量作用域的功能。这就好理解为什么:

f1();//0
f1();//1

2.我一开始认为f1和f2都=foo()是都指向了同一个function引用类型,所以顺理成章就会答错认为:

f2();//2

但其实foo()返回的是一个匿名函数,所以f1,f2相当于指向了两个不同的函数对象,所以结果也就顺理成章的变为:

f2();//0
编辑于 2018-05-26 14:54:57 回复(16)
var f1 = Foo();f1返回了一个匿名函数的调用,它可以访问到Foo()被调用时产生的环境,而局部变量i一直处在这个环境里。——《JavaScript设计模式与开发实践》
发表于 2019-02-28 14:49:09 回复(0)
两次调用f1()方法,然后i++即可获得结果
发表于 2018-12-28 13:31:47 回复(0)
Foo()指的是它里面的那个函数 :function(){ console.log(i++);},相当于var f1= function(){ console.log(i++);},所以f1执行,也就是console.log(i++)执行, 通过闭包获取的局部变量会一直存在于内存中, 不同的函数绑定不同的闭包,相同的函数绑定相同的闭包。
发表于 2017-05-03 09:49:24 回复(0)
foo函数两次分别将返回值也就是匿名函数赋值给f1 f2 ,即在堆中开辟了两个匿名函数的空间,f1 f2分别指向一个。因此两者作用域互不影响。 foo 里的匿名函数形成一个闭包,匿名函数作用域连同返回值一起进行了赋值,也就是开辟了两个var i =0空间。第一次调用,指向堆中的对应空间,输出0,因为是i++,i++,先输出再自家,此时i等于1,覆盖了作用域里的i,第二次调用输出1。当调用f2,他指向新的空间,空间中i 等于0,因戏输出0
发表于 2016-11-20 07:33:12 回复(0)
我怎么觉得这题的重点是i++与++i
发表于 2016-06-27 13:49:14 回复(1)
A f2的function 是一个新对象
发表于 2015-07-17 10:43:18 回复(0)
谁能解释一下答案
发表于 2015-03-23 16:23:47 回复(1)
首先返回的function函数赋值给全局变量f1,因此function函数就被储存在了内存中,因为foo函数是function函数的父函数,于是foo函数和局部变量i也被存在了内存。之后f1()被调用了两次,第一次调用时i=0,因为是i++,先输出i的值0,然后再++;第二次调用是i=1,所以先输出1;而f2是一个新的变量,因此i的值初始化为0。大致应该是这样吧。。。。。求指教
编辑于 2021-09-10 17:51:18 回复(35)
实践证明是0,1,2。
发表于 2017-10-16 20:15:24 回复(0)
        分别输出0,1,0;这里涉及到的js知识点也不是很深,主要就是++运算符的理解和闭包的理解。首先Foo函数返回的也是一个函数,即function( ){ console.log(i++),而这个函数的i引用的是外层函数的i,形成闭包,使得i的初始值为0。
var f1 = Foo( ),f2 = Foo( )这一句,前半句var f1 = Foo( )可以看做是var f1 = function( ){ console.log(i++)},。那么第一次执行f1的时候,自然是输出0,这里主要理解的是++运算符的使用,放在数字后表示后增,即先执行再+1,这里就是先执行console.log(i)之后i再加1,所以f1执行第二次的时候i已经变成了1,自然就输出1了。
后半句f2 = Foo() 可以看做是f2 = function( ){console.log(i++)},这没什么可说的,直接输出i的初始值0.(转自百度知道:https://zhidao.baidu.com/question/179922118885607404.html)
发表于 2017-01-10 12:49:12 回复(9)
答案是A
这道题就是JS的闭包
定义两个变量f1和f2用来接收Foo的返回值也就是Foo的一个子函数
在执行f1和f2时,会分别获取到父函数Foo中定义的局部变量i=0
第一次执行时,i++为0,i为1,第二次执行时,i++为1
发表于 2015-07-17 09:36:28 回复(4)
通过闭包获取的局部变量会一直存在于内存中
发表于 2016-01-20 19:59:16 回复(1)
f1();  --> 执行foo();  -->i++  (先赋值后++)  即此刻i=0;
f1();  -->再执行foo();-->由于上一次执行过i++;即此时i=1;
f2();  -->重新执行foo();   即i=0;
因此 选A;
发表于 2015-07-17 18:06:00 回复(3)
本题考查闭包
在一个函数的内部定义另一个函数,通过另一个函数来访问这个函数的局部变量,这是创建闭包的最常见的方式。而闭包会常驻内存(增大内存的使用量)所以,第一次调用f1后,i输出为0,但i++后,在第二次调用f1时,输出的i就为1了。另外,f2是f1都是同时声明的,二者地位相当,结果也都一样。
发表于 2016-01-04 22:34:21 回复(0)

问题

function Foo() {
    var i = 0;
    return function() {
        console.log(i);
    }
}

var f1 = Foo(),
    f2 = Foo();
f1();
f1();
f2();

输出的值是多少呢?

闭包

作用域的引用已某种方式被持有称为闭包
在上面的代码中,Foo()返回一个函数,该函数中有声明在Foo函数作用域中的变量i,虽然Foo函数已执行完毕,但我们可以通过调用f1函数获取i的值。这就叫闭包,f1函数保有了Foo函数的作用域,使得本应该被
销毁的作用域和变量都得以保留。

OK,那问题来了。f1, f2保有的Foo函数作用域是同一个作用域吗?

作用域(scope)

首先,我们应该明确js中只存在词法作用域,就是说作用域是在代码运行之前就确定了,作用域不会根据函数在何处被执行而改变。

观察上面的代码,可以看出f1, f2保有Foo函数的作用域都是同一作用域,作用域中都拥有变量i。

f1

<img src="https://www.simpleyin.xyz/doc/scopef1.png">

f2

<img src="https://www.simpleyin.xyz/doc/scopef2.png">

既然f1, f2都引用同一个作用域,那为什么i值会不相同?

执行上下文(execution context)

Ecma-262中对执行上下文的定义:

An execution context is a specification device that is used to track the runtime evaluation of code by an ECMAScript
implementation

An execution context contains whatever implementation specific state is necessary to track the execution progress of its associated code.

根据:

A new execution context is created whenever control is transferred from the executable code associated with the currently running execution context to executable code that is not associated with that execution context.

我们知道当 JavaScript 代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context).如var f1 = Foo(), 在执行Foo()时会创建一个新的上下文并将其压入执行上下文栈(见下一节)。

这个新的上下文可抽象表示为:

executionContextObj = {
    scopeChain: { /* 变量对象(variableObject)+ 所有父执行上下文的变量对象*/ }, 
    variableObject: { /*函数 arguments/参数,内部变量和函数声明 */ }, 
    this: {} 
}

由于每一次执行函数都会创建新的上下文,因此var f1 = Foo(), f2 = Foo();会创建两个不同的上下文,这两个上下文中也就包含了两个不相关的i变量。由于js属于词法作用域,执行f1()所创建的上下文就被包含在了Foo()所创建的上下文里,因此f1()中上下文的scopeChain绑定了Foo()上下文中的变量。

因此f1, f2的父上下文是两个不同的上下文,两个i变量独立,因此i值不同。题目输出的结果应该为:0 1 0

执行上下文栈(execution context stack)

Ecam-262定义:

The execution context stack is used to track execution contexts. The running execution context is always the top element of this stack.

执行环境

  • 全局环境,当js代码首次运行时进入的环境。
  • 函数环境,当函数被调用时,开始执行函数内代码时进入环境。
  • Eval, eval中的文本被执行时进入的环境。

参考

发表于 2018-07-19 20:22:08 回复(2)
首先,这是一个js中的闭包,一个函数有权操作另一个函数的变量和作用域,
这里,匿名函数有权访问foo函数的i变量,其次,需要知道foo与foo()的区别,
foo指的是整个foo函数的js代码,foo()指的是return 的语句。var f1=foo(),
那么f1实际上的值为匿名函数代码段,然后f1(),console中i++通俗的说是先赋值,
后自加,此时i=0,i++为1,再次调用f1(),根据闭包的特性,此时i=1,i++为2.而f2()
则相对于开辟了新的内存。

发表于 2016-05-22 15:21:42 回复(3)
A
发表于 2015-01-04 13:41:38 回复(9)
var f1=foo(); 将函数foo的执行结果赋给f1变量; foo()的执行结果就是他的回调函数function; 所以现在f1,f2两个变量都被存在了栈中,并且指向了堆中的function函数。 即f1=function(){console.log(i++)}; f2=function(){console.log(i++)}; f1和f2运行时各自产生自己的作用域。 第一次f1();执行function需要打印i++,但function函数局部并没有定义i,所以向上一级查询,就找到了foo中的var i=0; 这时,内存给i变量划分单元,值为0,然后console.log(i++);印值为0,内存单元中的i+1变为1。第二次f1();f1指向的function函数的局部已经有了i,且值为1,打印出1。执行f2();f2的function函数产生的作用域并没有i变量,所以向上一级寻找,最终打印出0。
编辑于 2017-10-30 12:30:59 回复(0)